library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.all;
use work.global_definition.all;

architecture behave of id_controller is
    type extension_method is(
        EX_U8, EX_INT,
        EX_SHIFT, EX_NONE,
        EX_S4, EX_S5,
        EX_S8, EX_S11
        );
begin
    process(ins)
    begin
        -- opcode code generation
        case ins(15 downto 11) is
            when "01001" =>
                opcode <= OP_ADDIU;                  --addiu
				im <= std_ulogic_vector(RESIZE(signed(ins(7 downto 0)), 16));
            when "01000" =>
                opcode <= OP_ADDIU3;                 --addiu3
				im <= std_ulogic_vector(RESIZE(signed(ins(3 downto 0)), 16));
            when "00000" =>
                opcode <= OP_ADDSP3;                 --addsp3 
				im <= std_ulogic_vector(RESIZE(signed(ins(7 downto 0)), 16));
            when "01100" =>
                case ins(10 downto 8) is
                    when "011" =>
                        opcode <= OP_ADDSP;          --addsp
						im <= std_ulogic_vector(RESIZE(signed(ins(7 downto 0)), 16));
                    when "000" =>
                        opcode <= OP_BTEQZ;          --bteqz
						im <= std_ulogic_vector(RESIZE(signed(ins(7 downto 0)), 16));
                    when "001" =>
                        opcode <= OP_BTNEZ;          --btnez
						im <= std_ulogic_vector(RESIZE(signed(ins(7 downto 0)), 16));
                    when "100" =>
                        opcode <= OP_MTSP;           --mtsp
						im <= (others => '0');
                    when "010" =>
                        opcode <= OP_SW_RS;          --sw_rs
						im <= std_ulogic_vector(RESIZE(signed(ins(7 downto 0)), 16));
                    when others =>
                        opcode <= OP_ERROR;          --error
						im <= (others => '0');
                end case;
            when "11100" =>
                case ins(1 downto 0) is
                    when "01" =>
                        opcode <= OP_ADDU;           --addu
						im <= (others => '0');
                    when "11" =>
                        opcode <= OP_SUBU;           --subu
						im <= (others => '0');
                    when others =>
                        opcode <= OP_ERROR;          --error
						im <= (others => '0');
                end case;
            when "11101" =>
                case ins(4 downto 0) is
                    when "01100" =>
                        opcode <= OP_AND;            --and
						im <= (others => '0');
                    when "01010" =>
                        opcode <= OP_CMP;            --cmp
						im <= (others => '0');
                    when "00000" =>
                        case ins(7 downto 5) is
                            when "110" =>
                                opcode <= OP_JALR;   --jalr
								im <= (others => '0');
                            when "000" =>
                                opcode <= OP_JR;     --jr
								im <= (others => '0');
                            when "001" =>
                                opcode <= OP_JRRA;   --jrra
								im <= (others => '0');
                            when "010" =>
                                opcode <= OP_MFPC;   --mfpc
								im <= (others => '0');
                            when others =>
                                opcode <= OP_ERROR;  --error
								im <= (others => '0');
                        end case;
                    when "01011" =>
                        opcode <= OP_NEG;            --neg
						im <= (others => '0');
                    when "01111" =>
                        opcode <= OP_NOT;            --not
						im <= (others => '0');
                    when "01101" =>
                        opcode <= OP_OR;             --or
						im <= (others => '0');
                    when "00100" =>
                        opcode <= OP_SLLV;           --sllv
						im <= (others => '0');
                    when "00010" =>
                        opcode <= OP_SLT;            --slt
						im <= (others => '0');
                    when "00011" =>
                        opcode <= OP_SLTU;           --sltu
						im <= (others => '0');
                    when "00111" =>
                        opcode <= OP_SRAV;           --srav
						im <= (others => '0');
                    when "00110" =>
                        opcode <= OP_SRLV;           --srlv
						im <= (others => '0');
                    when "01110" =>
                        opcode <= OP_XOR;            --xor
						im <= (others => '0');
                    when others =>
                        opcode <= OP_ERROR;          --error
						im <= (others => '0');
                end case;
            when "00010" =>
                opcode <= OP_B;                      --b
				im <= std_ulogic_vector(RESIZE(signed(ins(10 downto 0)), 16));
            when "00100" =>
                opcode <= OP_BEQZ;                   --beqz
				im <= std_ulogic_vector(RESIZE(signed(ins(7 downto 0)), 16));
            when "00101" =>
                opcode <= OP_BNEZ;                   --bnez
				im <= std_ulogic_vector(RESIZE(signed(ins(7 downto 0)), 16));
            when "01110" =>
                opcode <= OP_CMPI;                   --cmpi
				im <= std_ulogic_vector(RESIZE(signed(ins(7 downto 0)), 16));
            when "11111" =>
                opcode <= OP_INT;                    --int
				-- !!!!
				im <= (others => '0');
            when "01101" =>
                opcode <= OP_LI;                     --li
				im(7 downto 0)  <= ins(7 downto 0);
                im(15 downto 8) <= (others => '0');
            when "10011" =>
                opcode <= OP_LW;                     --lw
				im <= std_ulogic_vector(RESIZE(signed(ins(4 downto 0)), 16));
            when "10010" =>
                opcode <= OP_LW_SP;                  --lw_sp
				im <= std_ulogic_vector(RESIZE(signed(ins(7 downto 0)), 16));
            when "11110" =>
                case ins(7 downto 0) is
                    when "00000000" =>
                        opcode <= OP_MFIH;           --mfih
						im <= (others => '0');
                    when "00000001" =>
                        opcode <= OP_MTIH;           --mtih
						im <= (others => '0');
                    when others =>
                        opcode <= OP_ERROR;          --ERROR
						im <= (others => '0');
                end case;
            when "01111" =>
                opcode <= OP_MOVE;                   --move
				im <= (others => '0');
            when "00110" =>
                case INS(1 downto 0) is
                    when "00" =>
                        opcode <= OP_SLL;            --SLL
						im(15 downto 4) <= (others => '0');
		                im(2 downto 0)  <= ins(4 downto 2);
		                if (INS(4 downto 2) = "000") then
		                    im(3) <= '1';
		                else
		                    im(3) <= '0';
		                end if;
                    when "11" =>
                        opcode <= OP_SRA;            --sra
						im(15 downto 4) <= (others => '0');
		                im(2 downto 0)  <= ins(4 downto 2);
		                if (INS(4 downto 2) = "000") then
		                    im(3) <= '1';
		                else
		                    im(3) <= '0';
		                end if;
                    when "10" =>
                        opcode <= OP_SRL;            --srl
						im(15 downto 4) <= (others => '0');
		                im(2 downto 0)  <= ins(4 downto 2);
		                if (INS(4 downto 2) = "000") then
		                    im(3) <= '1';
		                else
		                    im(3) <= '0';
		                end if;
                    when others =>
                        opcode <= OP_ERROR;          --error
						im <= (others => '0');
                end case;
            when "01010" =>
                opcode <= OP_SLTI;                   --slti
				im <= std_ulogic_vector(RESIZE(signed(ins(7 downto 0)), 16));
            when "01011" =>
                opcode <= OP_SLTUI;                  --sltui
  				im <= std_ulogic_vector(RESIZE(unsigned(ins(7 downto 0)), 16));
            when "11011" =>
                opcode <= OP_SW;                     --sw
				im <= std_ulogic_vector(RESIZE(signed(ins(4 downto 0)), 16));
            when "11010" =>
                opcode <= OP_SW_SP;                  --sw_sp    
  				im <= std_ulogic_vector(RESIZE(signed(ins(7 downto 0)), 16));
            when "00001" =>
                opcode <= OP_NOP;                    --nop
				im <= (others => '0');
			when "00011" =>	--int 1,2,3
				case INS(10 downto 8) is
					when "001" =>
						opcode <= OP_INT1;
						im <= (others=>'1');
					when "010" =>
						opcode <= OP_INT2;
						im <= std_ulogic_vector(RESIZE(unsigned(ins(7 downto 0)), 16));
					when "100" =>
						opcode <= OP_INT3;
						im <= x"7FFF";
					when others=>
						opcode <= OP_ERROR;
						im <= (others=>'0');
				end case;
            when others =>
                opcode <= OP_ERROR;                  --error
				im <= (others => '0');
        end case;
 	end process;
end behave;
